home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / cvs-1_3.lha / cvs-1.3 / src / lock.c < prev    next >
C/C++ Source or Header  |  1992-04-10  |  12KB  |  523 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.3 kit.
  7.  * 
  8.  * Set Lock
  9.  * 
  10.  * Lock file support for CVS.
  11.  */
  12.  
  13. #include "cvs.h"
  14.  
  15. #ifndef lint
  16. static char rcsid[] = "@(#)lock.c 1.42 92/04/10";
  17. #endif
  18.  
  19. extern char *ctime ();
  20.  
  21. #if __STDC__
  22. static int readers_exist (char *repository);
  23. static int set_lock (char *lockdir, int will_wait, char *repository);
  24. static void set_lockers_name (struct stat *statp);
  25. static int set_writelock_proc (Node * p);
  26. static int unlock_proc (Node * p);
  27. static int write_lock (char *repository);
  28. static void unlock (char *repository);
  29. static void lock_wait ();
  30. #else
  31. static int unlock_proc ();
  32. static void unlock ();
  33. static int set_writelock_proc ();
  34. static int write_lock ();
  35. static int readers_exist ();
  36. static int set_lock ();
  37. static void set_lockers_name ();
  38. static void lock_wait ();
  39. #endif                /* __STDC__ */
  40.  
  41. static char lockers_name[20];
  42. static char *repository;
  43. static char readlock[PATH_MAX], writelock[PATH_MAX];
  44. static int cleanup_lckdir;
  45. static List *locklist;
  46.  
  47. #define L_OK        0        /* success */
  48. #define L_ERROR        1        /* error condition */
  49. #define L_LOCK_OWNED    2        /* lock already owned by us */
  50. #define L_LOCKED    3        /* lock owned by someone else */
  51.  
  52. /*
  53.  * Clean up all outstanding locks
  54.  */
  55. void
  56. Lock_Cleanup ()
  57. {
  58.     /* clean up simple locks (if any) */
  59.     if (repository != NULL)
  60.     {
  61.     unlock (repository);
  62.     repository = (char *) NULL;
  63.     }
  64.  
  65.     /* clean up multiple locks (if any) */
  66.     if (locklist != (List *) NULL)
  67.     {
  68.     (void) walklist (locklist, unlock_proc);
  69.     locklist = (List *) NULL;
  70.     }
  71. }
  72.  
  73. /*
  74.  * walklist proc for removing a list of locks
  75.  */
  76. static int
  77. unlock_proc (p)
  78.     Node *p;
  79. {
  80.     unlock (p->key);
  81.     return (0);
  82. }
  83.  
  84. /*
  85.  * Remove the lock files (without complaining if they are not there),
  86.  */
  87. static void
  88. unlock (repository)
  89.     char *repository;
  90. {
  91.     char tmp[PATH_MAX];
  92.     struct stat sb;
  93.  
  94.     if (readlock[0] != '\0')
  95.     {
  96.     (void) sprintf (tmp, "%s/%s", repository, readlock);
  97.     (void) unlink (tmp);
  98.     }
  99.  
  100.     if (writelock[0] != '\0')
  101.     {
  102.     (void) sprintf (tmp, "%s/%s", repository, writelock);
  103.     (void) unlink (tmp);
  104.     }
  105.  
  106.     /*
  107.      * Only remove the lock directory if it is ours, note that this does
  108.      * lead to the limitation that one user ID should not be committing
  109.      * files into the same Repository directory at the same time. Oh well.
  110.      */
  111.     (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
  112.     if (stat (tmp, &sb) != -1 && sb.st_uid == geteuid () &&
  113.     (writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir)))
  114.     {
  115.     (void) rmdir (tmp);
  116.     }
  117.     cleanup_lckdir = 0;
  118. }
  119.  
  120. /*
  121.  * Create a lock file for readers
  122.  */
  123. int
  124. Reader_Lock (xrepository)
  125.     char *xrepository;
  126. {
  127.     int err = 0;
  128.     FILE *fp;
  129.     char tmp[PATH_MAX];
  130.  
  131.     if (noexec)
  132.     return (0);
  133.  
  134.     /* we only do one directory at a time for read locks! */
  135.     if (repository != NULL)
  136.     {
  137.     error (0, 0, "Reader_Lock called while read locks set - Help!");
  138.     return (1);
  139.     }
  140.  
  141.     if (readlock[0] == '\0')
  142.     (void) sprintf (readlock, "%s.%d", CVSRFL, getpid ());
  143.  
  144.     /* remember what we're locking (for lock_cleanup) */
  145.     repository = xrepository;
  146.  
  147.     /* make sure we clean up on error */
  148.     (void) SIG_register (SIGHUP, Lock_Cleanup);
  149.     (void) SIG_register (SIGINT, Lock_Cleanup);
  150.     (void) SIG_register (SIGQUIT, Lock_Cleanup);
  151.     (void) SIG_register (SIGPIPE, Lock_Cleanup);
  152.     (void) SIG_register (SIGTERM, Lock_Cleanup);
  153.  
  154.     /* make sure we can write the repository */
  155.     (void) sprintf (tmp, "%s/%s.%d", xrepository, CVSTFL, getpid ());
  156.     if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
  157.     {
  158.     error (0, errno, "cannot create read lock in repository `%s'",
  159.            xrepository);
  160.     readlock[0] = '\0';
  161.     (void) unlink (tmp);
  162.     return (1);
  163.     }
  164.     (void) unlink (tmp);
  165.  
  166.     /* get the lock dir for our own */
  167.     (void) sprintf (tmp, "%s/%s", xrepository, CVSLCK);
  168.     if (set_lock (tmp, 1, xrepository) != L_OK)
  169.     {
  170.     error (0, 0, "failed to obtain dir lock in repository `%s'",
  171.            xrepository);
  172.     readlock[0] = '\0';
  173.     return (1);
  174.     }
  175.  
  176.     /* write a read-lock */
  177.     (void) sprintf (tmp, "%s/%s", xrepository, readlock);
  178.     if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
  179.     {
  180.     error (0, errno, "cannot create read lock in repository `%s'",
  181.            xrepository);
  182.     readlock[0] = '\0';
  183.     err = 1;
  184.     }
  185.  
  186.     /* free the lock dir */
  187.     (void) sprintf (tmp, "%s/%s", xrepository, CVSLCK);
  188.     if (rmdir (tmp) < 0)
  189.     error (0, errno, "failed to remove lock dir `%s'", tmp);
  190.  
  191.     return (err);
  192. }
  193.  
  194. /*
  195.  * Lock a list of directories for writing
  196.  */
  197. static char *lock_error_repos;
  198. static int lock_error;
  199. int
  200. Writer_Lock (list)
  201.     List *list;
  202. {
  203.     if (noexec)
  204.     return (0);
  205.  
  206.     /* We only know how to do one list at a time */
  207.     if (locklist != (List *) NULL)
  208.     {
  209.     error (0, 0, "Writer_Lock called while write locks set - Help!");
  210.     return (1);
  211.     }
  212.  
  213.     for (;;)
  214.     {
  215.     /* try to lock everything on the list */
  216.     lock_error = L_OK;        /* init for set_writelock_proc */
  217.     lock_error_repos = (char *) NULL; /* init for set_writelock_proc */
  218.     locklist = list;        /* init for Lock_Cleanup */
  219.     (void) strcpy (lockers_name, "unknown");
  220.  
  221.     (void) walklist (list, set_writelock_proc);
  222.  
  223.     switch (lock_error)
  224.     {
  225.         case L_ERROR:        /* Real Error */
  226.         Lock_Cleanup ();    /* clean up any locks we set */
  227.         error (0, 0, "lock failed - giving up");
  228.         return (1);
  229.  
  230.         case L_LOCKED:        /* Someone already had a lock */
  231.         Lock_Cleanup ();    /* clean up any locks we set */
  232.         lock_wait (lock_error_repos); /* sleep a while and try again */
  233.         continue;
  234.  
  235.         case L_OK:            /* we got the locks set */
  236.         return (0);
  237.  
  238.         default:
  239.         error (0, 0, "unknown lock status %d in Writer_Lock",
  240.                lock_error);
  241.         return (1);
  242.     }
  243.     }
  244. }
  245.  
  246. /*
  247.  * walklist proc for setting write locks
  248.  */
  249. static int
  250. set_writelock_proc (p)
  251.     Node *p;
  252. {
  253.     /* if some lock was not OK, just skip this one */
  254.     if (lock_error != L_OK)
  255.     return (0);
  256.  
  257.     /* apply the write lock */
  258.     lock_error_repos = p->key;
  259.     lock_error = write_lock (p->key);
  260.     return (0);
  261. }
  262.  
  263. /*
  264.  * Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if
  265.  * lock held by someone else or L_ERROR if an error occurred
  266.  */
  267. static int
  268. write_lock (repository)
  269.     char *repository;
  270. {
  271.     int status;
  272.     FILE *fp;
  273.     char tmp[PATH_MAX];
  274.  
  275.     if (writelock[0] == '\0')
  276.     (void) sprintf (writelock, "%s.%d", CVSWFL, getpid ());
  277.  
  278.     /* make sure we clean up on error */
  279.     (void) SIG_register (SIGHUP, Lock_Cleanup);
  280.     (void) SIG_register (SIGINT, Lock_Cleanup);
  281.     (void) SIG_register (SIGQUIT, Lock_Cleanup);
  282.     (void) SIG_register (SIGPIPE, Lock_Cleanup);
  283.     (void) SIG_register (SIGTERM, Lock_Cleanup);
  284.  
  285.     /* make sure we can write the repository */
  286.     (void) sprintf (tmp, "%s/%s.%d", repository, CVSTFL, getpid ());
  287.     if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
  288.     {
  289.     error (0, errno, "cannot create write lock in repository `%s'",
  290.            repository);
  291.     (void) unlink (tmp);
  292.     return (L_ERROR);
  293.     }
  294.     (void) unlink (tmp);
  295.  
  296.     /* make sure the lock dir is ours (not necessarily unique to us!) */
  297.     (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
  298.     status = set_lock (tmp, 0, repository);
  299.     if (status == L_OK || status == L_LOCK_OWNED)
  300.     {
  301.     /* we now own a writer - make sure there are no readers */
  302.     if (readers_exist (repository))
  303.     {
  304.         /* clean up the lock dir if we created it */
  305.         if (status == L_OK)
  306.         {
  307.         if (rmdir (tmp) < 0)
  308.             error (0, errno, "failed to remove lock dir `%s'", tmp);
  309.         }
  310.  
  311.         /* indicate we failed due to read locks instead of error */
  312.         return (L_LOCKED);
  313.     }
  314.  
  315.     /* write the write-lock file */
  316.     (void) sprintf (tmp, "%s/%s", repository, writelock);
  317.     if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
  318.     {
  319.         int xerrno = errno;
  320.  
  321.         (void) unlink (tmp);
  322.         /* free the lock dir if we created it */
  323.         if (status == L_OK)
  324.         {
  325.         (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
  326.         if (rmdir (tmp) < 0)
  327.             error (0, errno, "failed to remove lock dir `%s'", tmp);
  328.         }
  329.  
  330.         /* return the error */
  331.         error (0, xerrno, "cannot create write lock in repository `%s'",
  332.            repository);
  333.         return (L_ERROR);
  334.     }
  335.     return (L_OK);
  336.     }
  337.     else
  338.     return (status);
  339. }
  340.  
  341. /*
  342.  * readers_exist() returns 0 if there are no reader lock files remaining in
  343.  * the repository; else 1 is returned, to indicate that the caller should
  344.  * sleep a while and try again.
  345.  */
  346. static int
  347. readers_exist (repository)
  348.     char *repository;
  349. {
  350.     char line[MAXLINELEN];
  351.     DIR *dirp;
  352.     struct direct *dp;
  353.     struct stat sb;
  354.     CONST char *regex_err;
  355.     int ret = 0;
  356.  
  357. #ifdef CVS_FUDGELOCKS
  358. again:
  359. #endif
  360.  
  361.     if ((dirp = opendir (repository)) == NULL)
  362.     error (1, 0, "cannot open directory %s", repository);
  363.  
  364.     (void) sprintf (line, "^%s.*", CVSRFL);
  365.     if ((regex_err = re_comp (line)) != NULL)
  366.     error (1, 0, "%s", regex_err);
  367.  
  368.     while ((dp = readdir (dirp)) != NULL)
  369.     {
  370.     (void) sprintf (line, "%s/%s", repository, dp->d_name);
  371.     if (re_exec (dp->d_name))
  372.     {
  373. #ifdef CVS_FUDGELOCKS
  374.         time_t now;
  375.  
  376.         (void) time (&now);
  377.  
  378.         /*
  379.          * If the create time of the file is more than CVSLCKAGE seconds
  380.          * ago, try to clean-up the lock file, and if successful, re-open
  381.          * the directory and try again.
  382.          */
  383.         if (stat (line, &sb) != -1)
  384.         {
  385.         if (now >= (sb.st_ctime + CVSLCKAGE) && unlink (line) != -1)
  386.         {
  387.             (void) closedir (dirp);
  388.             goto again;
  389.         }
  390.         set_lockers_name (&sb);
  391.         }
  392. #else
  393.         if (stat (line, &sb) != -1)
  394.         set_lockers_name (&sb);
  395. #endif
  396.         ret = 1;
  397.         break;
  398.     }
  399.     }
  400.     (void) closedir (dirp);
  401.     return (ret);
  402. }
  403.  
  404. /*
  405.  * Set the static variable lockers_name appropriately, based on the stat
  406.  * structure passed in.
  407.  */
  408. static void
  409. set_lockers_name (statp)
  410.     struct stat *statp;
  411. {
  412.     struct passwd *pw;
  413.  
  414.     if ((pw = (struct passwd *) getpwuid (statp->st_uid)) !=
  415.     (struct passwd *) NULL)
  416.     {
  417.     (void) strcpy (lockers_name, pw->pw_name);
  418.     }
  419.     else
  420.     (void) sprintf (lockers_name, "uid%d", statp->st_uid);
  421. }
  422.  
  423. /*
  424.  * Persistently tries to make the directory "lckdir",, which serves as a
  425.  * lock. If the create time on the directory is greater than CVSLCKAGE
  426.  * seconds old, just try to remove the directory.
  427.  */
  428. static int
  429. set_lock (lockdir, will_wait, repository)
  430.     char *lockdir;
  431.     int will_wait;
  432.     char *repository;
  433. {
  434.     struct stat sb;
  435. #ifdef CVS_FUDGELOCKS
  436.     time_t now;
  437. #endif
  438.  
  439.     /*
  440.      * Note that it is up to the callers of set_lock() to arrange for signal
  441.      * handlers that do the appropriate things, like remove the lock
  442.      * directory before they exit.
  443.      */
  444.     cleanup_lckdir = 0;
  445.     for (;;)
  446.     {
  447.     SIG_beginCrSect ();
  448.     if (mkdir (lockdir, 0777) == 0)
  449.     {
  450.         cleanup_lckdir = 1;
  451.         SIG_endCrSect ();
  452.         return (L_OK);
  453.     }
  454.     SIG_endCrSect ();
  455.  
  456.     if (errno != EEXIST)
  457.     {
  458.         error (0, errno,
  459.            "failed to create lock directory in repository `%s'",
  460.            repository);
  461.         return (L_ERROR);
  462.     }
  463.  
  464.     /*
  465.      * stat the dir - if it is non-existent, re-try the loop since
  466.      * someone probably just removed it (thus releasing the lock)
  467.      */
  468.     if (stat (lockdir, &sb) < 0)
  469.     {
  470.         if (errno == ENOENT)
  471.         continue;
  472.  
  473.         error (0, errno, "couldn't stat lock directory `%s'", lockdir);
  474.         return (L_ERROR);
  475.     }
  476.  
  477.     /*
  478.      * if we already own the lock, go ahead and return 1 which means it
  479.      * existed but we owned it
  480.      */
  481.     if (sb.st_uid == geteuid () && !will_wait)
  482.         return (L_LOCK_OWNED);
  483.  
  484. #ifdef CVS_FUDGELOCKS
  485.  
  486.     /*
  487.      * If the create time of the directory is more than CVSLCKAGE seconds
  488.      * ago, try to clean-up the lock directory, and if successful, just
  489.      * quietly retry to make it.
  490.      */
  491.     (void) time (&now);
  492.     if (now >= (sb.st_ctime + CVSLCKAGE))
  493.     {
  494.         if (rmdir (lockdir) >= 0)
  495.         continue;
  496.     }
  497. #endif
  498.  
  499.     /* set the lockers name */
  500.     set_lockers_name (&sb);
  501.  
  502.     /* if he wasn't willing to wait, return an error */
  503.     if (!will_wait)
  504.         return (L_LOCKED);
  505.     lock_wait (repository);
  506.     }
  507. }
  508.  
  509. /*
  510.  * Print out a message that the lock is still held, then sleep a while.
  511.  */
  512. static void
  513. lock_wait (repos)
  514.     char *repos;
  515. {
  516.     time_t now;
  517.  
  518.     (void) time (&now);
  519.     error (0, 0, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
  520.        lockers_name, repos);
  521.     (void) sleep (CVSLCKSLEEP);
  522. }
  523.